package com.mask.im.business.handler;


import com.mask.im.business.common.exception.BusinessException;
import com.mask.im.business.common.model.RestResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.io.IOException;
import java.sql.SQLException;
import java.util.stream.Collectors;

/**
 * 全局异常处理器
 * 统一处理系统异常，提供友好的错误响应
 * 
 * @author jian
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    // 错误码常量
    private static final int BUSINESS_ERROR_CODE = 40001;
    private static final int PARAM_ERROR_CODE = 40002;
    private static final int SYSTEM_ERROR_CODE = 50001;
    private static final int DATABASE_ERROR_CODE = 50002;
    private static final int IO_ERROR_CODE = 50003;
    private static final int NOT_FOUND_ERROR_CODE = 40401;
    private static final int METHOD_NOT_SUPPORTED_ERROR_CODE = 40501;

    // 错误信息常量
    private static final String PARAM_FORMAT_ERROR = "参数格式异常";
    private static final String PARAM_MISSING_ERROR = "缺少必要参数";
    private static final String PARAM_TYPE_ERROR = "参数类型错误";
    private static final String SYSTEM_ERROR = "系统内部错误";
    private static final String DATABASE_ERROR = "数据库操作异常";
    private static final String IO_ERROR = "文件操作异常";
    private static final String NOT_FOUND_ERROR = "请求的资源不存在";
    private static final String METHOD_NOT_SUPPORTED_ERROR = "请求方法不支持";

    /**
     * 业务异常处理
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public RestResponse<?> handleBusinessException(BusinessException e, HttpServletRequest request) {
        log.warn("业务异常: {} - {}", e.getCode(), e.getReason(), e);
        logRequestInfo(request);
        return RestResponse.fail(e.getCode(), e.getReason());
    }

    /**
     * 参数校验异常 - @RequestBody
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public RestResponse<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
        String errorMessage = e.getBindingResult().getAllErrors().stream()
                .map(DefaultMessageSourceResolvable::getDefaultMessage)
                .collect(Collectors.joining("; "));
        
        log.warn("参数校验失败: {} - {}", request.getRequestURI(), errorMessage);
        logRequestInfo(request);
        return RestResponse.fail(PARAM_ERROR_CODE, errorMessage);
    }

    /**
     * 参数校验异常 - @ModelAttribute
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public RestResponse<?> handleBindException(BindException e, HttpServletRequest request) {
        String errorMessage = e.getBindingResult().getAllErrors().stream()
                .map(DefaultMessageSourceResolvable::getDefaultMessage)
                .collect(Collectors.joining("; "));
        
        log.warn("参数绑定失败: {} - {}", request.getRequestURI(), errorMessage);
        logRequestInfo(request);
        return RestResponse.fail(PARAM_ERROR_CODE, errorMessage);
    }

    /**
     * 参数校验异常 - @RequestParam
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public RestResponse<?> handleConstraintViolationException(ConstraintViolationException e, HttpServletRequest request) {
        String errorMessage = e.getConstraintViolations().stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.joining("; "));
        
        log.warn("参数约束校验失败: {} - {}", request.getRequestURI(), errorMessage);
        logRequestInfo(request);
        return RestResponse.fail(PARAM_ERROR_CODE, errorMessage);
    }

    /**
     * 参数格式异常
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public RestResponse<?> handleHttpMessageNotReadableException(HttpMessageNotReadableException e, HttpServletRequest request) {
        log.warn("参数格式异常: {} - {}", request.getRequestURI(), e.getMessage());
        logRequestInfo(request);
        return RestResponse.fail(PARAM_ERROR_CODE, PARAM_FORMAT_ERROR);
    }

    /**
     * 缺少请求参数异常
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public RestResponse<?> handleMissingServletRequestParameterException(MissingServletRequestParameterException e, HttpServletRequest request) {
        log.warn("缺少请求参数: {} - 参数名: {}, 参数类型: {}", request.getRequestURI(), e.getParameterName(), e.getParameterType());
        logRequestInfo(request);
        return RestResponse.fail(PARAM_ERROR_CODE, PARAM_MISSING_ERROR + ": " + e.getParameterName());
    }

    /**
     * 参数类型不匹配异常
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public RestResponse<?> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) {
        log.warn("参数类型不匹配: {} - 参数名: {}, 期望类型: {}, 实际值: {}", 
                request.getRequestURI(), e.getName(), e.getRequiredType(), e.getValue());
        logRequestInfo(request);
        return RestResponse.fail(PARAM_ERROR_CODE, PARAM_TYPE_ERROR + ": " + e.getName());
    }

    /**
     * 请求方法不支持异常
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public RestResponse<?> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
        log.warn("请求方法不支持: {} - 当前方法: {}, 支持的方法: {}", 
                request.getRequestURI(), e.getMethod(), String.join(", ", e.getSupportedMethods()));
        logRequestInfo(request);
        return RestResponse.fail(METHOD_NOT_SUPPORTED_ERROR_CODE, METHOD_NOT_SUPPORTED_ERROR);
    }

    /**
     * 404异常
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public RestResponse<?> handleNoHandlerFoundException(NoHandlerFoundException e, HttpServletRequest request) {
        log.warn("请求的资源不存在: {} - {}", request.getRequestURI(), e.getMessage());
        logRequestInfo(request);
        return RestResponse.fail(NOT_FOUND_ERROR_CODE, NOT_FOUND_ERROR);
    }

    /**
     * 数据库异常
     */
    @ExceptionHandler({DataAccessException.class, SQLException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public RestResponse<?> handleDatabaseException(Exception e, HttpServletRequest request) {
        log.error("数据库操作异常: {} - {}", request.getRequestURI(), e.getMessage(), e);
        logRequestInfo(request);
        return RestResponse.fail(DATABASE_ERROR_CODE, DATABASE_ERROR);
    }

    /**
     * IO异常
     */
    @ExceptionHandler(IOException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public RestResponse<?> handleIOException(IOException e, HttpServletRequest request) {
        log.error("IO操作异常: {} - {}", request.getRequestURI(), e.getMessage(), e);
        logRequestInfo(request);
        return RestResponse.fail(IO_ERROR_CODE, IO_ERROR);
    }

    /**
     * 空指针异常
     */
    @ExceptionHandler(NullPointerException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public RestResponse<?> handleNullPointerException(NullPointerException e, HttpServletRequest request) {
        log.error("空指针异常: {} - {}", request.getRequestURI(), e.getMessage(), e);
        logRequestInfo(request);
        return RestResponse.fail(SYSTEM_ERROR_CODE, SYSTEM_ERROR);
    }

    /**
     * 参数异常
     */
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public RestResponse<?> handleIllegalArgumentException(IllegalArgumentException e, HttpServletRequest request) {
        log.warn("参数异常: {} - {}", request.getRequestURI(), e.getMessage());
        logRequestInfo(request);
        return RestResponse.fail(PARAM_ERROR_CODE, e.getMessage());
    }

    /**
     * 运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public RestResponse<?> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
        log.error("运行时异常: {} - {}", request.getRequestURI(), e.getMessage(), e);
        logRequestInfo(request);
        return RestResponse.fail(SYSTEM_ERROR_CODE, SYSTEM_ERROR);
    }

    /**
     * 通用异常处理
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public RestResponse<?> handleException(Exception e, HttpServletRequest request) {
        log.error("未知异常: {} - {}", request.getRequestURI(), e.getMessage(), e);
        logRequestInfo(request);
        return RestResponse.fail(SYSTEM_ERROR_CODE, SYSTEM_ERROR);
    }

    /**
     * 记录请求信息
     */
    private void logRequestInfo(HttpServletRequest request) {
        log.debug("请求信息 - URI: {}, Method: {}, IP: {}, User-Agent: {}", 
                request.getRequestURI(), 
                request.getMethod(), 
                getClientIpAddress(request),
                request.getHeader("User-Agent"));
    }

    /**
     * 获取客户端IP地址
     */
    private String getClientIpAddress(HttpServletRequest request) {
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) {
            return xForwardedFor.split(",")[0].trim();
        }
        
        String xRealIp = request.getHeader("X-Real-IP");
        if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) {
            return xRealIp;
        }
        
        return request.getRemoteAddr();
    }
}
